#include "myled.h"
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/uaccess.h>

#define LED1_ON                                   \
    do {                                          \
        gpioe_virl_base_addr->ODR |= (0x1 << 10); \
    } while (0)
#define LED1_OFF                                   \
    do {                                           \
        gpioe_virl_base_addr->ODR &= ~(0x1 << 10); \
    } while (0)
int major = 0; // 主设备号
char kbuf[128] = { 0 };
struct class* cls = NULL;
struct device* dev = NULL;

volatile unsigned int* rcc_virl_addr;
volatile gpio_t* gpioe_virl_base_addr;
volatile gpio_t* gpiof_virl_base_addr;

int myled_open(struct inode* inode, struct file* file_accessed)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}
long myled_ioctl(struct file* file, unsigned int cmd, unsigned long arg)
{
    int ret;
    int witch;
    switch (cmd) {
    case LED_ON:
        // 获取用户空间传递给内核空间的数据
        ret = copy_from_user(&witch, (void*)arg, GET_CMD_SIZE(LED_ON));
        if (ret) {
            printk("from user space copy data to kernel space failed\n");
            return -EINVAL;
        }
        printk("LED%d  LED%d_ON\n", witch, witch);
        if (witch == 1)
            LED1_ON;
        break;
    case LED_OFF:
        // 获取用户空间传递给内核空间的数据
        ret = copy_from_user(&witch, (void*)arg, GET_CMD_SIZE(LED_OFF));
        if (ret) {
            printk("from user space copy data to kernel space failed\n");
            return -EINVAL;
        }
        printk("LED%d  LED%d_OFF\n", witch, witch);
        if (witch == 1)
            LED1_OFF;
        break;
    case UACCESS_STRING:
        // 获取用户空间传递给内核空间的数据
        ret = copy_from_user(kbuf, (void*)arg, GET_CMD_SIZE(UACCESS_STRING));
        if (ret) {
            printk("from user space copy data to kernel space failed\n");
            return -EINVAL;
        }
        printk("kernel space kbuf = %s\n", kbuf);
        break;
    }
    return 0;
}
int myled_close(struct inode* inode, struct file* file)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}
// 定义操作方法结构体的变量，并对成员进行初始化
struct file_operations fops = {
    .open = myled_open,
    .unlocked_ioctl = myled_ioctl,
    .release = myled_close,
};

int led_init(void)
{
    // 完成物理地址到虚拟地址的映射
    rcc_virl_addr = ioremap(RCC_PHY_ADDR, 4);
    if (rcc_virl_addr == NULL) {
        printk("phy addr to virl addr failed\n");
        return -ENOMEM;
    }

    gpioe_virl_base_addr = ioremap(GPIOE_BASE_ADDR, sizeof(gpio_t));
    if (gpioe_virl_base_addr == NULL) {
        printk("phy addr to virl addr failed\n");
        return -ENOMEM;
    }

    gpiof_virl_base_addr = ioremap(GPIOF_BASE_ADDR, sizeof(gpio_t));
    if (gpiof_virl_base_addr == NULL) {
        printk("phy addr to virl addr failed\n");
        return -ENOMEM;
    }
    // GPIOE和GPIOF外设时钟的使能
    *rcc_virl_addr = (0x3 << 4);
    // LED1灯的初始化
    gpioe_virl_base_addr->MODER &= (~(0x3 << 20));
    gpioe_virl_base_addr->MODER |= (0x1 << 20);
    gpioe_virl_base_addr->OTYPER &= (~(0x1 << 10));
    gpioe_virl_base_addr->OSPEEDR &= (~(0x3 << 20));
    gpioe_virl_base_addr->PUPDR &= (~(0x3 << 20));
    gpioe_virl_base_addr->ODR &= (~(0x1 << 10));
    // LED2灯的初始化
    gpiof_virl_base_addr->MODER &= (~(0x3 << 20));
    gpiof_virl_base_addr->MODER |= (0x1 << 20);
    gpiof_virl_base_addr->OTYPER &= (~(0x1 << 10));
    gpiof_virl_base_addr->OSPEEDR &= (~(0x3 << 20));
    gpiof_virl_base_addr->PUPDR &= (~(0x3 << 20));
    gpiof_virl_base_addr->ODR &= (~(0x1 << 10));
    // LED3灯的初始化
    gpioe_virl_base_addr->MODER &= (~(0x3 << 16));
    gpioe_virl_base_addr->MODER |= (0x1 << 16);
    gpioe_virl_base_addr->OTYPER &= (~(0x1 << 8));
    gpioe_virl_base_addr->OSPEEDR &= (~(0x3 << 16));
    gpioe_virl_base_addr->PUPDR &= (~(0x3 << 16));
    gpioe_virl_base_addr->ODR &= (~(0x1 << 8));
    return 0;
}

void led_delInit(void)
{
    iounmap(gpioe_virl_base_addr);
    iounmap(gpiof_virl_base_addr);
    iounmap(rcc_virl_addr);
}

static int __init myled_init(void)
{
    int ret;
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    // 注册字符设备
    major = register_chrdev(0, CNAME, &fops);
    if (major < 0) {
        printk("register char device failed!\n");
        return major;
    }
    printk("register char device successed, major = %d\n", major);

    ret = led_init();
    if (ret != 0) {
        return ret;
    }
    // 向上层提交目录的信息
    cls = class_create(THIS_MODULE, "hello");
    if (IS_ERR(cls)) {
        printk("create class failed\n");
        return PTR_ERR(cls);
    }
    // 向上层提交设备信息
    dev = device_create(cls, NULL, MKDEV(major, 0), NULL, "myled");
    if (IS_ERR(dev)) {
        printk("create device failed\n");
        return PTR_ERR(dev);
    }
    return 0;
}

static void __exit myled_exit(void)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    // 销毁设备信息
    device_destroy(cls, MKDEV(major, 0));
    // 销毁目录的信息
    class_destroy(cls);
    // 先销毁虚拟的映射的地址
    led_delInit();
    // 销毁字符设备
    unregister_chrdev(major, CNAME);
}

module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");
